#!/usr/bin/env node
"use strict";
/**
 * rStaticGlobalConfig rconf
 */
const fs = require("fs");
const path = require("path");
const cli = require("cac")();
const chalk = require("chalk");

// 配置表
const configs = {
  // 配置文件路径，相对于 sourceDirPath
  tempConfigPath: [],
  // 模版根目录地址
  templateDirPath: [],
  // 各个环境下静态文件对应地址 相对 env 目录的地址
  tempStaticDir: [],
  tempDefaultConfigPath: [],
  // 修正根路径
  root: [],
  // env对应目录
  envDir: undefined,
  // 对应环境需要注入的变量
  envOpts: {},
  // 目标静态文件地址
  targetStaticDir: [],
  // 目标配置文件地址 ../a/b/c.js
  targetConfigPath: [],
  configFomatter: function (config) {
    return `${JSON.stringify(config, null, " ")}`;
  },
};

const getRootDirname = () => path.join(process.cwd(), ...configs.root);
function getDirName(dirList = []) {
  return path.join(getRootDirname(), ...dirList);
}
cli
  .command("run", "复制模版文件，合并配置文件到目标目录")
  .option("--env <env>", "模板环境")
  .option("--root <root>", "根目录相对地址")
  .option("--templateDirPath <templateDirPath>", "模板根文件夹路径")
  .option(
    "--tempDefaultConfigPath <tempDefaultConfigPath>",
    "模板根文件夹下的公共配置文件路径"
  )
  .option("--tempConfigPath <tempConfigPath>", "模板文件相对模板文件夹路径")
  .option("--tempStaticDir <tempStaticDir>", "模板静态文件夹相对环境目录的路径")
  .option("--targetConfigPath <targetConfigPath>", "配置文件的目标路径")
  .option("--targetStaticDir <targetStaticDir>", "静态文件目标目录路径")
  .action((options) => {
    delete options["--"];
    handlerVariables(options);
  });

cli.help();
cli.version("1.0.2");

cli.parse();

function getPathFromString(key, str) {
  if (!str) return [];
  if (Array.isArray(str)) return str;
  if (typeof str === "string") return str.split("/").filter((t) => !!t);
  throw new Error(`请为${key}配置字符串或者字符数组`);
}

// 处理变量
function handlerVariables(options) {
  let rconfConfig = {};
  const confPath = getDirName([".rconf.js"]);
  if (fs.existsSync(confPath)) {
    rconfConfig = require(confPath);
  }
  const packagePath = getDirName(["package.json"]);
  let packageJson = {};
  if (fs.existsSync(packagePath)) {
    packageJson = require(packagePath);
  }
  rconfConfig = Object.assign(
    {},
    rconfConfig,
    packageJson.rconf || {},
    options
  );
  if (
    rconfConfig.configFomatter &&
    typeof rconfConfig.configFomatter !== "function"
  ) {
    throw new Error(`configFomatter 必须是函数`);
  }

  rconfConfig.configFomatter =
    rconfConfig.configFomatter || configs.configFomatter;

  Object.keys(configs).forEach((key) => {
    const val = rconfConfig[key];
    if (!["envDir", "configFomatter"].includes(key)) {
      configs[key] = getPathFromString(key, val);
    } else {
      configs[key] = val;
    }
  });
  configs.envOpts = rconfConfig.opts || {};
  configs.envDir = rconfConfig.env || {};

  handler(configs.envDir || "", configs.envOpts);
}

// 清除静态文件
function clear() {
  if (
    Array.isArray(configs.targetStaticDir) &&
    configs.targetStaticDir.length > 0
  ) {
    // 先找原静态文件
    const dirs = fs.readdirSync(getDirName(configs.templateDirPath));
    const filenams = [];
    if (Array.isArray(dirs)) {
      dirs.forEach((dir) => {
        const dirPath = getDirName([...configs.templateDirPath, dir]);
        if (fs.statSync(dirPath).isDirectory()) {
          //   读取原文件静态文件夹下的文件列表
          const filePath = path.join(dirPath, ...configs.tempStaticDir);
          if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
            filenams.push(fs.readdirSync(filePath));
          }
        }
      });
    }
    const needClearFiles = filenams.reduce((p, c) => p.concat(c), []);
    // 删除所有可能存在的静态文件
    needClearFiles.forEach((filename) => {
      //   读取目标静态文件夹下文件
      const filepath = getDirName([...configs.targetStaticDir, filename]);
      if (filename && fs.existsSync(filepath)) {
        // 删除源头文件
        console.log(chalk.red(`删除文件：${filepath}`));
        fs.unlinkSync(filepath);
      }
    });
  }
  if (
    Array.isArray(configs.targetStaticDir) &&
    configs.targetStaticDir.length > 0
  ) {
    // 删除目标配置文件
    const tconfigPath = getDirName(configs.targetConfigPath);
    if (
      Array.isArray(configs.targetConfigPath) &&
      configs.targetConfigPath.length &&
      fs.existsSync(tconfigPath)
    ) {
      // 删除目标 config 文件
      console.log(chalk.red(`删除文件：${tconfigPath}`));
      fs.unlinkSync(tconfigPath);
    }
  }
}

// 合并模版配置文件
function mergeObj(o, n) {
  if (isObject(n) && isObject(o)) {
    Object.keys(n).forEach((key) => {
      o[key] = mergeObj(o[key], n[key]);
    });
    return o;
  }
  return n;
}

function mergeConfigs(...args) {
  return args.reduce(mergeObj, {});
}

function isObject(o) {
  return Object.prototype.toString.call(o) === "[object Object]";
}

// 写入最新的配置文件
function writeConfig(envDir, opts = {}) {
  if (
    !Array.isArray(configs.targetConfigPath) ||
    !configs.targetConfigPath.length
  ) {
    console.log(chalk.yellow(`无配置文件输出`));
    return;
  }
  let tempConfig = {};
  let defaultConfig = {};

  const defaultConfigPath = getDirName([
    ...configs.templateDirPath,
    ...configs.tempDefaultConfigPath,
  ]);
  if (
    fs.existsSync(defaultConfigPath) &&
    fs.statSync(defaultConfigPath).isFile()
  ) {
    defaultConfig = require(defaultConfigPath);
  }
  const confPath = getDirName([
    ...configs.templateDirPath,
    envDir,
    ...configs.tempConfigPath,
  ]);
  if (fs.existsSync(confPath) && fs.statSync(confPath).isFile()) {
    tempConfig = require(confPath);
  }
  const configJson = mergeConfigs(defaultConfig, tempConfig, opts);
  const targetConfigPath = [...configs.targetConfigPath];
  targetConfigPath.pop();
  checkAndCreateDir(getDirName(), targetConfigPath);
  const targetPath = getDirName(configs.targetConfigPath);
  console.log(chalk.green(`生成文件: ${targetPath}`));
  fs.writeFileSync(targetPath, configs.configFomatter(configJson));
}
// 复制静态文件到目标目录
function copyFiles(envDir) {
  if (!Array.isArray(configs.tempStaticDir) || !configs.tempStaticDir.length) {
    console.log(chalk.yellow(`无静态文件需要拷贝`));
    return;
  }
  const tempEnvStaticPath = getDirName([
    ...configs.templateDirPath,
    envDir,
    ...configs.tempStaticDir,
  ]);
  if (
    !fs.existsSync(tempEnvStaticPath) ||
    !fs.statSync(tempEnvStaticPath).isDirectory()
  ) {
    return console.log(chalk.yellow(`${envDir} 无静态文件输出`));
  }
  const imgs = fs.readdirSync(tempEnvStaticPath);
  if (Array.isArray(imgs)) {
    imgs.forEach((filename) => {
      const src = path.join(tempEnvStaticPath, filename);
      checkAndCreateDir(getDirName(), configs.targetStaticDir);
      const dest = getDirName([...configs.targetStaticDir, filename]);
      console.log(chalk.blue(`复制文件: ${src}`));
      console.log(chalk.green(`生成文件: ${dest}`));
      fs.copyFileSync(src, dest);
    });
  }
}

// 命令处理
function handler(envDir, opts) {
  try {
    clear();
    writeConfig(envDir, opts);
    copyFiles(envDir);
  } catch (e) {
    console.log(e);
  }
}

// 校验和创建目录
function checkAndCreateDir(rootPath, dirPath) {
  let dirName = rootPath;
  dirPath.forEach((dir) => {
    dirName = path.join(dirName, dir);
    if (!fs.existsSync(dirName) || !fs.statSync(dirName).isDirectory()) {
      console.log(chalk.green(`生成文件夹：${dirName}`));
      fs.mkdirSync(dirName);
    }
  });
}
